﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.IO;

namespace DirectPOS.Printer {
  public static class RawPrinter {
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public class DocInfo {
      [MarshalAs(UnmanagedType.LPStr)]
      public string mDocName;

      [MarshalAs(UnmanagedType.LPStr)]
      public string mOutputFile;

      [MarshalAs(UnmanagedType.LPStr)]
      public string mDataType;
    }

    [DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string aPrinter, out IntPtr oPrinter, IntPtr aDefaults);

    [DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool ClosePrinter(IntPtr aHandle);

    [DllImport("winspool.Drv", EntryPoint = "StartDocPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool StartDocPrinter(IntPtr aHandle, Int32 aLevel, [In, MarshalAs(UnmanagedType.LPStruct)] DocInfo aDocInfo);

    [DllImport("winspool.Drv", EntryPoint = "EndDocPrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool EndDocPrinter(IntPtr aHandle);

    [DllImport("winspool.Drv", EntryPoint = "StartPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool StartPagePrinter(IntPtr aHandle);

    [DllImport("winspool.Drv", EntryPoint = "EndPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool EndPagePrinter(IntPtr aHandle);

    [DllImport("winspool.Drv", EntryPoint = "WritePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool WritePrinter(IntPtr aHandle, IntPtr aBytes, Int32 aCount, out Int32 oWritten);

    // Returns true on success, false on failure.
    public static void Send(string aPrinterName, IntPtr aBytes, Int32 aCount) {
      var xDocInfo = new DocInfo();
      xDocInfo.mDocName = "POS Print";
      xDocInfo.mDataType = "RAW";

      IntPtr xHandle;
      if (OpenPrinter(aPrinterName.Normalize(), out xHandle, IntPtr.Zero)) {
        try {
          if (StartDocPrinter(xHandle, 1, xDocInfo)) {
            if (StartPagePrinter(xHandle)) {
              Int32 xWritten;
              if (!WritePrinter(xHandle, aBytes, aCount, out xWritten)) {
                Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error());
              }
              EndPagePrinter(xHandle);
            }
            EndDocPrinter(xHandle);
          }
        } finally {
          ClosePrinter(xHandle);
        }
      }
    }

    public static void Send(string aPrinterName, byte[] aBytes) {
      var xBytes = Marshal.AllocCoTaskMem(aBytes.Length); try {
        Marshal.Copy(aBytes, 0, xBytes, aBytes.Length); 
        Send(aPrinterName, xBytes, aBytes.Length);
      } finally {
        Marshal.FreeCoTaskMem(xBytes);
      }
    }

    public static void Send(string aPrinterName, string aString) {
      // Most POS Printers only take ANSI, so we convert to ANSI
      var xBytes = Marshal.StringToCoTaskMemAnsi(aString); try {
        Send(aPrinterName, xBytes, aString.Length);
      } finally {
        Marshal.FreeCoTaskMem(xBytes);
      }
    }
  }
}
